package com.ihr360.payroll.service.salarytasklet.step;

import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import com.ihr360.alps.api.ServiceProvider;
import com.ihr360.common.DateRange;
import com.ihr360.commons.lang.CollectionUtils;
import com.ihr360.commons.model.meta.table.DynamicCell;
import com.ihr360.payroll.calc.StaffSalaryCalcPeriod;
import com.ihr360.payroll.calc.StaffSalaryCalcPeriodTerm;
import com.ihr360.payroll.dao.ReferencePropertyRepository;
import com.ihr360.payroll.dao.ReferenceServiceRepository;
import com.ihr360.payroll.model.dto.reference.ReferenceService;
import com.ihr360.payroll.service.alps.AlpsSyncService;
import com.ihr360.payroll.service.salarytasklet.SalaryReferenceDataService;
import com.ihr360.payroll.service.staff.StaffPayrollInfoService;
import com.ihr360.validation.ValidationUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;

/**
 * @author davidwei
 */
@Service
public class SalaryReferenceDataServiceImpl implements SalaryReferenceDataService {
    @Autowired
    private StaffPayrollInfoService staffPayrollInfoService;

    @Autowired
    private AlpsSyncService alpsSyncService;

    @Autowired
    private ReferenceServiceRepository refServiceRepository;

    @Autowired
    ReferencePropertyRepository refPropertyRepository;


    /**
     * 获取员工数据（社保公积金，员工基本信息，获取员工薪资体系数据，根据ReferenceApplication中的服务定义获取数据）
     *
     * @param salaryReferences 依赖的服务信息（包含需要需要引用的服务的meta信息）
     * @param staffIds   员工数据
     * @return
     */
    @Override
    public Map<String, StaffSalaryCalcPeriod> loadSalaryPeriodsOfStaffs(String companyId, final Set<String> salaryReferences,
                                                                        Collection<String> staffIds, DateRange payrollPeriodRange, DateRange attendancePeriodRange) {
        ValidationUtils.rejectIfEmpty(payrollPeriodRange, null, "薪资周期范围未设定");
        ValidationUtils.rejectIfEmpty(attendancePeriodRange, null, "考勤周期范围未设定");
        ValidationUtils.rejectIfEmpty(payrollPeriodRange.getStart(), null, "薪资周期起始日未设定");
        ValidationUtils.rejectIfEmpty(payrollPeriodRange.getEnd(), null, "考勤周期截止日未设定");

        if (CollectionUtils.isEmpty(salaryReferences)) {
            return new HashMap<>(0);
        }
        Set<String> copiedReference = new HashSet<>(salaryReferences);
        DateRange unionRange = DateRange.union(payrollPeriodRange, attendancePeriodRange);
        Map<String, StaffSalaryCalcPeriod> map = staffPayrollInfoService.getPeriodSalaryProfileOfStaffs(companyId, staffIds, unionRange);
        filterReferencedFields(copiedReference, map);
        return map;
    }

    /**
     * 过滤未被引用的字段
     *
     * @param copiedReference
     * @param map
     */
    private void filterReferencedFields(Set<String> copiedReference, Map<String, StaffSalaryCalcPeriod> map) {
        for (StaffSalaryCalcPeriod period : map.values()) {
            if (period == null) {
                continue;
            }
            List<StaffSalaryCalcPeriodTerm> terms = period.getPhases();
            if (CollectionUtils.isEmpty(terms)) {
                continue;
            }

            for (StaffSalaryCalcPeriodTerm t : terms) {
                Map<String, DynamicCell> items = t.getItems();
                if (CollectionUtils.isEmpty(items)) {
                    return;
                }
                Map<String, DynamicCell> newItems = new HashMap<>(copiedReference.size());
                for (String code : copiedReference) {
                    newItems.put(code, items.get(code));
                }
                t.setItems(newItems);
            }
            period.setPhases(terms);
        }
    }

    @Override
    public Map<String, Map<String, Map<String, Object>>> loadReferencedDataOfStaffs(String companyId, final Map<String, Set<String>> references,
                                                                                    Collection<String> staffIds, DateRange payrollPeriodRange, DateRange attendancePeriodRange) {
        ValidationUtils.rejectIfEmpty(references, null, "引用服务数据列表定义为空");
        Map<String, Set<String>> filteredReference = filterInnerServiceReference(references);
        if (CollectionUtils.isEmpty(filteredReference)) {
            return Collections.emptyMap();
        }
        Iterable<ReferenceService> refServicesIterable = refServiceRepository.findAll();

        Map<String, ReferenceService> serviceMap = StreamSupport.stream(refServicesIterable.spliterator(), false)
                .collect(Collectors.toMap(ReferenceService::getRefId, x -> x));

        /**
         * key: staffId
         * value:{ key: serviceid,value:{fieldName,fieldValue}}
         */
        Map<String, Map<String, Map<String, Object>>> dataMap = Maps.newConcurrentMap();
        for (Map.Entry<String, Set<String>> entry : filteredReference.entrySet()) {
            String serviceId = entry.getKey();
            ReferenceService referenceService = serviceMap.get(serviceId);
            if (referenceService != null) {
                Map<String, Map<String, Object>> serviceData = alpsSyncService.getDataByService(companyId, referenceService, staffIds, payrollPeriodRange, attendancePeriodRange);

                if (CollectionUtils.isNotEmpty(serviceData)) {

                    for (Map.Entry<String, Map<String, Object>> rowEntry : serviceData.entrySet()) {
                        String staffId = rowEntry.getKey();
                        Map<String, Object> rowData = rowEntry.getValue();
                        if (dataMap.containsKey(staffId)) {
                            Map<String, Map<String, Object>> concurrentServiceData = dataMap.get(staffId);
                            concurrentServiceData.put(referenceService.getRefId(), rowData);
                        } else {
                            Map<String, Map<String, Object>> concurrentServiceData = new ConcurrentHashMap<>(10);
                            concurrentServiceData.put(referenceService.getRefId(), rowData);
                            dataMap.put(staffId, concurrentServiceData);
                        }
                    }
                }
            }
        }
        return dataMap;
    }

    private ImmutableMap<String, Set<String>> filterInnerServiceReference(Map<String, Set<String>> copiedReference) {
        ImmutableMap.Builder<String, Set<String>> builder = ImmutableMap.builder();
        for (Map.Entry<String, Set<String>> entry : copiedReference.entrySet()) {
            switch (entry.getKey()) {
                case ServiceProvider.SERVICE_ID.STAFF_SALARY:
                case ServiceProvider.SERVICE_ID.SYS_TAX_TABLE:
                case ServiceProvider.SERVICE_ID.SALARY_ADJUST_RULE:
                case ServiceProvider.SERVICE_ID.SALARY_OT_RULE:
                case ServiceProvider.SERVICE_ID.SALARY_PL_RULE:
                case ServiceProvider.SERVICE_ID.SALARY_SL_RULE:
                case ServiceProvider.SERVICE_ID.ATTENDANCE_DAIRY_REPORT:
                case ServiceProvider.SERVICE_ID.ATTENDANCE_MONTHLY_REPORT:
                case ServiceProvider.SERVICE_ID.ATTENDANCE_PERIOD_REPORT:
                    continue;
            }
            builder.put(entry.getKey(), entry.getValue());
        }
        return builder.build();
    }
}